#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <stdio.h>

#include "FwRule.h"
#include "FwConfig.h"
#include "FwEngine.h"


int initDivSock(int port);

int main(int argc, char *argv[]){

	int divport = 0;
	fwRuleNode* rulesHead = 0;
	fwRuleList* rulesList = 0;	
	FILE* logfile = 0;

	char FILEBUF[128];

	int parseResult = 0;

	if(argc != 3){
		printf("usage: fw564 [configfile] [div port]\n");
		return 1;
	}
	parseResult = parseConfig(argv[1], &rulesList, &rulesHead);
	divport = atoi(argv[2]);	

	if(parseResult == 1){
		printf("Invalid rule file!\n");
		return 1;
	}

	int divsock = initDivSock(divport);

	if(divsock == -1){
		printf("Unable to create divert socket\n");
		return 1;
	}

	logfile = fopen("logfile.txt", "a+");

	setbuffer(logfile, &FILEBUF[0], 128);

	const int buflen = 2048;
	int pktlen = 0;
	unsigned char buf[buflen];
	struct sockaddr_in insockaddr;
	unsigned int addrsize = sizeof(struct sockaddr);
	fwRuleNode* matchedRule = 0;
	while(1){
		pktlen = recvfrom(divsock, buf, buflen, 0, (struct sockaddr *) &insockaddr, &addrsize);
		if(pktlen > 0){
			matchedRule = matchPkt(buf, pktlen, rulesList);	
			if(matchedRule == 0){  //none matched
				sendto(divsock, buf, pktlen, 0, (struct sockaddr *) &insockaddr, addrsize);
			}
			else{
				if(strcmp(matchedRule->action, "ALLOW") == 0){
					sendto(divsock, buf, pktlen, 0, (struct sockaddr *) &insockaddr, addrsize);
				}
				if(strcmp(matchedRule->log, "yes") == 0){
					logpkt(buf, pktlen, logfile, matchedRule); 					
				}
			}
		}
	}

	//do clean up, closing log file, divsock
}

/*
 * initializes divertsocket and returns the socket file descriptor
 * returns -1 if unable to initialize
 */
int initDivSock(int port){
	struct sockaddr_in addr;
	int sockfd;

	sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT);
	if(sockfd == -1){
		return -1;
	}

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(port);

	if (bind(sockfd, (struct sockaddr*) &addr, sizeof addr) == -1){
		return -1;
	}

	return sockfd;
}
